﻿using Jacobi.Vst.Core;
using Levfier.Proccessors.Parameters;
using System;
using System.Collections.Generic;

namespace Levfier.Proccessors.Chains
{
    /// <summary>
    /// プラグインの入力 OR 出力に使用するストリーム
    /// </summary>
    [Serializable]
    public class BoundChain : Chain, IProccessable
    {
        /// <summary>
        /// 出力値の上限
        /// </summary>
        private static readonly double MaxValue= 1.0d;

        /// <summary>
        /// 出力値の下限
        /// </summary>
        private static readonly double MinValue= -1.0d;

        /// <summary>
        /// 値が変更された時のイベントハンドラ
        /// </summary>
        public event ValueChanged OutputVolumeChanged;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="sampleCount">サンプル数</param>
        /// <param nameparam name="input">入力チェインの場合か否か</param>
        public BoundChain(int sampleCount, bool input) 
            : base()
        {
            base.buffer = new double[sampleCount];
            if (!input)
            {
                this.SetProccessor(this);
            }
            VSTParameterManager.GetInstance().SampleCountChanged += this.ChangeSampleCount;
        }

        /// <summary>
        /// sourceBufferからバッファに値をコピーする
        /// </summary>
        /// <param name="sourceBuffer">入力バッファ</param>
        public void CopyFrom(VstAudioBuffer sourceBuffer)
        {
            int length = Math.Min(base.buffer.Length, sourceBuffer.SampleCount);
            for (int i = 0; i < length; i++)
            {
                this.buffer[i] = sourceBuffer[i];
            }
        }

        /// <summary>
        /// targetBufferにバッファの値をコピーする
        /// </summary>
        /// <param name="targetBuffer">入力バッファ</param>
        public void CopyTo(VstAudioBuffer targetBuffer)
        {
            int length = Math.Min(base.buffer.Length, targetBuffer.SampleCount);
            for (int i = 0; i < length; i++)
            {
                targetBuffer[i] = (float)this.buffer[i];
            }
        }

        /// <summary>
        /// 処理を行う
        /// 出力値が上下限を超えていないかチェックし、値を範囲内に修正する
        /// </summary>
        /// <param name="input">入力</param>
        public void Proccess(double[] input)
        {
            //ピークメータ用の最大値保持変数
            double maxVolume = 0.0d;

            for (int i = 0; i < input.Length; i++)
            {
                double temp = input[i];
                double absTemp = Math.Abs(temp);

                //上限下限に値を修正
                if(temp > MaxValue)
                {
                    temp = MaxValue;
                }
                else if (temp < MinValue)
                {
                    temp = MinValue;
                }

                if (maxVolume < absTemp)
                {
                    maxVolume = absTemp;
                }

                //出力バッファに書き込み
                this.buffer[i] = temp;
            }

            //アウトプットの最大値を伝える。
            if( null != this.OutputVolumeChanged )
            {
                this.OutputVolumeChanged(maxVolume);
            }
        }

        /// <summary>
        /// 使用しない
        /// </summary>
        /// <param name="stream">使用しない/param>
        public void SetLowerChain(IChainable stream)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// 使用しない
        /// </summary>
        public void RemoveLowerChain()
        {
            //呼び出されたら実装ミス
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// 空のコレクションを返す
        /// </summary>
        /// <returns>空のコレクション</returns>
        public System.Collections.Generic.IDictionary<string, Parameters.ProccessParameter> GetParameters()
        {
            return new Dictionary<string, Parameters.ProccessParameter>();
        }

        /// <summary>
        /// サンプルカウントが変更された時の処理
        /// </summary>
        /// <param name="value">変更後の値</param>
        private void ChangeSampleCount(double value)
        {
            base.buffer = new double[(int)value];
        }

        /// <summary>
        /// 何もしない
        /// </summary>
        /// <param name="chain">上流チェイン</param>
        public void NotifyConnect(IChainable chain)
        {
            //throw new NotImplementedException();
        }

        /// <summary>
        /// 何もしない
        /// </summary>
        /// <param name="chain">上流チェイン</param>
        public void NotifyDisconnect(IChainable chain)
        {
            //throw new NotImplementedException();
        }
    }
}
